#from django.utils.datetime_safe import datetime
from StringIO import StringIO
#from django.template.defaultfilters import time
import os, datetime, time
from google.appengine.api import mail
from google.appengine.ext import webapp, db
from google.appengine.ext.webapp import template
from google.appengine.api import users, urlfetch, memcache
from django.utils import simplejson

import sys, os, logging
sys.path.insert(0, __file__.split(os.sep + "GMT" + os.sep)[0])

import import_wrapper
from GMT.messaging.xmlpickle import pickle, unpickle
from GMT.messaging import messages
#from GMT.appengine.relativedelta import relativedelta

MINER_URL = "http://www.cs.tau.ac.il/cgi-bin/cgiwrap/~nirgrin2/forward.py"
#MINER_URL = "http://localhost:40404"

class UserInfo(db.Model):
    username = db.StringProperty()
    password = db.StringProperty()
    error_code = db.IntegerProperty()
    week_topsenders = db.TextProperty()
    week_toprecipients = db.TextProperty()
    week_topsize = db.TextProperty()
    week_timeofday = db.TextProperty()
    week_lastupdated = db.DateTimeProperty()
    fourweek_topsenders = db.TextProperty()
    fourweek_toprecipients = db.TextProperty()
    fourweek_topsize = db.TextProperty()
    fourweek_timeofday = db.TextProperty()
    fourweek_lastupdated = db.DateTimeProperty()
    ever_topsenders = db.TextProperty()
    ever_toprecipients = db.TextProperty()
    ever_topsize = db.TextProperty()
    ever_timeofday = db.TextProperty()
    ever_lastupdated = db.DateTimeProperty() 
    
    @staticmethod
    def get_by_username(username):
        return UserInfo.gql("WHERE username = :1", username).get()
#    get_by_username = staticmethod(get_by_username)

    @staticmethod
    def get_by_weekdate(dt):
        return UserInfo.gql("WHERE error_code = -1 and week_lastupdated < :1", dt.strftime("DATETIME(%Y, %m, %d, %H, %M, %S)"))

    @staticmethod
    def get_by_fourweekdate(dt):
        return UserInfo.gql("WHERE error_code = -1 and fourweek_lastupdated < :1", dt.strftime("DATETIME(%Y, %m, %d, %H, %M, %S)"))    

    @staticmethod
    def get_by_everdate(dt):
        return UserInfo.gql("WHERE error_code = -1 and ever_lastupdated < :1", dt.strftime("DATETIME(%Y, %m, %d, %H, %M, %S)"))

    @staticmethod
    def send_email(username, subject):
        try:            
            mail.send_mail(sender="myGmailTrends Support <mgmltrnds@googlemail.com>",
                  to=username,
                  subject=subject,
                  body="""
Dear myGmailTrends user:

Your account data has been updated. You can now visit
http://www.igoogle.com/ and view the latest results using our gadget.

Please let us know if you have any questions.

The myGmailTrends Team
""")
        except Exception, exp:
            logging.error(str(exp))
            
    def update(request, response, userinfo=None):
        if userinfo is None:
            userinfo = UserInfo.get_by_username(request.username)
#            if userinfo is None:
#                userinfo = UserInfo()
#                userinfo.username = request.username
#                userinfo.password = request.password
        
        userinfo.error_code = -1
        updatetime = datetime.datetime.now()        
        if response.time_span == "WEEK":
            if response.data.has_key("Top messages by size"):
                userinfo.week_topsize = response.data["Top messages by size"]
            if response.data.has_key("Top senders"):
                userinfo.week_topsenders = response.data["Top senders"]
            if response.data.has_key("Top recipients"):
                userinfo.week_toprecipients = response.data["Top recipients"]
            if response.data.has_key("Time of day"):
                userinfo.week_timeofday = response.data["Time of day"]
            userinfo.week_lastupdated = updatetime
        elif response.time_span == "4WEEKS":
            if response.data.has_key("Top messages by size"):
                userinfo.fourweek_topsize = response.data["Top messages by size"]
            if response.data.has_key("Top senders"):
                userinfo.fourweek_topsenders = response.data["Top senders"]
            if response.data.has_key("Top recipients"):
                userinfo.fourweek_toprecipients = response.data["Top recipients"]
            if response.data.has_key("Time of day"):
                userinfo.fourweek_timeofday = response.data["Time of day"]
            if userinfo.fourweek_lastupdated is None:
                UserInfo.send_email(userinfo.username, "Your personal 4-weeks statistics are now available at myGmailTrends!")
            userinfo.fourweek_lastupdated = updatetime
        elif response.time_span == "EVER":
            if response.data.has_key("Top messages by size"):
                userinfo.ever_topsize = response.data["Top messages by size"]
            if response.data.has_key("Top senders"):
                userinfo.ever_topsenders = response.data["Top senders"]
            if response.data.has_key("Top recipients"):
                userinfo.ever_toprecipients = response.data["Top recipients"]
            if response.data.has_key("Time of day"):
                userinfo.ever_timeofday = response.data["Time of day"]
            if userinfo.ever_lastupdated is None:
                UserInfo.send_email(userinfo.username, "Your full personal statistics are now available at myGmailTrends!")
            userinfo.ever_lastupdated = updatetime
        userinfo.put()
        return userinfo
    update = staticmethod(update)
    
    def needs_weekly_update(self):
        if self.week_lastupdated:
            delta = datetime.datetime.now() - self.week_lastupdated
            return delta.days > 1
        else:
            return True
    
    def get_json(self):
        if self.error_code is None or self.error_code < 0:
            results = {"WeekTop messages by size": self.week_topsize, 
                       "WeekTop senders": self.week_topsenders, 
                       "WeekTop recipients": self.week_toprecipients,
                       "WeekTime of day": self.week_timeofday,
                       "4WeekTop messages by size": self.fourweek_topsize, 
                       "4WeekTop senders": self.fourweek_topsenders, 
                       "4WeekTop recipients": self.fourweek_toprecipients,
                       "4WeekTime of day": self.fourweek_timeofday,
                       "EverTop messages by size": self.ever_topsize, 
                       "EverTop senders": self.ever_topsenders, 
                       "EverTop recipients": self.ever_toprecipients,
                       "EverTime of day": self.ever_timeofday}
        else:
            results = {"code": self.error_code}
        return simplejson.dumps(results)
#        json = StringIO()
#        json.write("{")
#        i = len(results)
#        for key, value in results.items():
#            if value is None:
#                value = "null"
#            if i != 1:
#                json.write("\"%s\": \"%s\", " % (key, value))
#            else:
#                json.write("\"%s\": \"%s\"" % (key, value))
#            i = i-1
#        json.write("}")
#        return json.getvalue()
    
def fetch_sync_response(sync_request, requests, userinfo):
    try:
        res = urlfetch.fetch(MINER_URL,        
                                 method=urlfetch.POST,
                                 payload=pickle(requests),
                                 headers={'Content-Type': 'text/xml'},
                                 deadline=10)
        logging.info(str(res.status_code))
        if res.status_code != 200:
            raise Exception("could not reach minning server")
        response = unpickle(res.content)
        return UserInfo.update(sync_request, response, userinfo)
    except urlfetch.DownloadError:
        for i in range(6):
            time.sleep(5)
            new_userinfo = UserInfo.get_by_username(sync_request.username)
            if not new_userinfo is None and \
                not new_userinfo.week_lastupdated is None and \
                (userinfo.week_lastupdated is None or new_userinfo.week_lastupdated > userinfo.week_lastupdated):
                return new_userinfo                
    raise GMTException(999, "User request didn't complete in a timely fashion")

def fetch_async(requests):
    res = urlfetch.fetch(MINER_URL,        
                             method=urlfetch.POST,
                             payload=pickle(requests),
                             headers={'Content-Type': 'text/xml'},
                             deadline=10)
    if res.status_code != 200:
        # TODO: send an email that async/cron request has failed.
        raise GMTException(2, "could not reach minning server")



class MainPage(webapp.RequestHandler):
#    """ Renders the main template."""
#    def get(self): 
#        user = users.get_current_user()
#        if user:
#            #greeting = ("Welcome, %s! (<a href=\"%s\">sign out</a>)" %
#            #            (user.nickname(), users.create_logout_url("/")))
#            template_values = { 'title':("Welcome, %s! (<a href=\"%s\">sign out</a>)" % (user.nickname(), users.create_logout_url("/"))), } 
#            path = os.path.join(os.path.dirname(__file__), "index.html")
#            self.response.out.write(template.render(path, template_values))
#        else:
#            greeting = ("<a href=\"%s\">Sign in or register</a>." % users.create_login_url("/"))
#            self.response.out.write("<html><body>%s</body></html>" % greeting)

    def get(self):              
        q = UserInfo.all()
        userinfo = q.get()
        if userinfo is None:
            self.response.out.write("No results")
            return
        else:
            self.response.out.write(userinfo.get_json())
        return
            
    def post(self):
        try:            
            args = self.request.arguments()
            if not ('username' in args):
                raise Exception("'username' must be provided")
            if not 'password' in args:
                raise Exception("'password' must be provided")
            username = self.request.get('username') # TODO: consider lower() 
            password = self.request.get('password')
            logging.debug('got request for user: %s pass: %s' % (username, password))
            if password is None or len(password) < 1:
                raise GMTException(1, "invalid password")
            userinfo = UserInfo.get_by_username(username)
            is_new = False
            if userinfo is None:
                is_new = True
                userinfo = UserInfo()
                userinfo.username = username
                userinfo.password = password
                userinfo.error_code = 0
                userinfo.week_lastupdated = None
                userinfo.put()
            else:
                if userinfo.password != password:
                    raise GMTException(1, "invalid password")        
            
            if is_new:
                logging.debug("fecthing new user info")
                requests = self.build_newuser_requests(userinfo) 
                fetch_async(requests)                
#            elif userinfo.needs_weekly_update():
#                logging.info("fecthing weekly update")
#                sync_request, requests = self.build_weekupdate_request(userinfo)           
#                userinfo = fetch_sync_response(sync_request, requests, userinfo) 
            else:
                logging.debug("returning db data")
                
            logging.debug(userinfo.get_json())
            self.response.out.write(userinfo.get_json())
        except GMTException, exp:
            logging.info("got gmtexp")
            self.report_error(exp)
            return
        except Exception, exp:
            logging.info("got exp")
            logging.error(exp.args[0]) 
            return
    
    @staticmethod        
    def build_newuser_requests(userinfo):
#        sync_request = messages.Request(userinfo.username, userinfo.password, True)
#        sync_request.time_span = "WEEK"
        async_request1 = messages.Request(userinfo.username, userinfo.password, True, True)
        async_request1.time_span = "WEEK"
        async_request2 = messages.Request(userinfo.username, userinfo.password, True, True)
        async_request2.time_span = "4WEEKS"
        requests = [async_request1, async_request2]
#        requests = [sync_request, async_request1, async_request2]
        memclient = memcache.Client()
#        memclient.add(sync_request.id, sync_request, 60*3)
        memclient.add(async_request1.id, async_request1, 60*5)
        memclient.add(async_request2.id, async_request2, 60*20)
        return requests
    
    @staticmethod
    def build_weekupdate_request(userinfo):
        sync_request = messages.Request(userinfo.username, userinfo.password, True)
        sync_request.time_span = "WEEK"
        requests = [sync_request]
        memclient = memcache.Client()
        memclient.add(sync_request.id, sync_request, 60*3)
        return (sync_request, requests,)
            
    def report_error(self, gmt_exp):
        self.response.out.write(gmt_exp.toJSON())
        
class PostPage(webapp.RequestHandler):
    def post(self):
        xml_in = self.request.body
        response = unpickle(xml_in)
        # TODO: remove this memcache id
        memclient = memcache.Client()
        request = memclient.get(response.id)
        userinfo = UserInfo.update(request, response)
        self.response.out.write(userinfo.get_json()) 
        
class DailyUpdate(webapp.RequestHandler):
    def get(self):
        cut_date = datetime.datetime.now() - datetime.timedelta(days=1)
        requests = list()
        memclient = memcache.Client()
        q = UserInfo.get_by_weekdate(cut_date)
        for userinfo in q:
            request = messages.Request(userinfo.username, userinfo.password, True, True)
            request.time_span = "WEEK"
            requests.append(request)
            memclient.add(request.id, request, 60*10)
          
        fetch_async(requests)
        
class WeeklyUpdate(webapp.RequestHandler):
    def get(self):
        cut_date = datetime.datetime.now() - datetime.timedelta(days=7)
        requests = list()
        memclient = memcache.Client()
        q = UserInfo.get_by_fourweekdate(cut_date)
        for userinfo in q:
            request = messages.Request(userinfo.username, userinfo.password, True, True)
            request.time_span = "4WEEKS"
            requests.append(request)
            memclient.add(request.id, request, 60*20)
          
        fetch_async(requests)
        
class EverUpdate(webapp.RequestHandler):
    def get(self):
        logging.debug("sending update EVER requests...")
        cut_date = datetime.datetime.now() - datetime.timedelta(weeks=1)
        logging.debug("updating entries older than "+str(cut_date))
        requests = list()
        memclient = memcache.Client()
        q = UserInfo.get_by_everdate(cut_date)
        for userinfo in q:
            logging.debug("update EVER for user: %s pass: %s" % (userinfo.username, userinfo.password))
            request = messages.Request(userinfo.username, userinfo.password, True, True)
            request.time_span = "EVER"
            requests.append(request)
            memclient.add(request.id, request, 60*30)
          
        fetch_async(requests)    
        logging.debug("sending update EVER requests done.")    
        
class GMTException(Exception):
    def __init__(self, code, msg):
        Exception.__init__(self)
        self.code = code
        self.msg = msg
    def toJSON(self):
        results = {"code": self.code}#, "msg": self.msg}
        return simplejson.dumps(results)